<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"fengxun2017.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.13.2","exturl":false,"sidebar":{"position":"right","display":"hide","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="FreeRTOS提供了几个钩子（Hook）函数接口（本质是设置一个回调函数）。如果我们按照钩子函数要求的形式实现它，就可以让内核在特定状态，或内核发生了特定事件时，调用我们实现的钩子函数。">
<meta property="og:type" content="article">
<meta property="og:title" content="FreeRTOS-内核中的钩子(Hook)函数">
<meta property="og:url" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/index.html">
<meta property="og:site_name" content="fx">
<meta property="og:description" content="FreeRTOS提供了几个钩子（Hook）函数接口（本质是设置一个回调函数）。如果我们按照钩子函数要求的形式实现它，就可以让内核在特定状态，或内核发生了特定事件时，调用我们实现的钩子函数。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/hook_register.jpg">
<meta property="og:image" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/idle-hook-error.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/stack-check-1.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/stack-check-2.png">
<meta property="article:published_time" content="2022-11-13T16:00:00.000Z">
<meta property="article:modified_time" content="2023-02-02T14:06:19.405Z">
<meta property="article:author" content="feng xun">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/hook_register.jpg">


<link rel="canonical" href="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/","path":"2022/11/14/FreeRTOS-kernal-hook/","title":"FreeRTOS-内核中的钩子(Hook)函数"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>FreeRTOS-内核中的钩子(Hook)函数 | fx</title>
  






  <script async defer data-website-id="" src=""></script>

  <script defer data-domain="" src=""></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
  <a target="_blank" rel="noopener" href="https://github.com/fengxun2017" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">fx</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-4"><a class="nav-link" href="#1%EF%BC%9A%E4%BB%80%E4%B9%88%E6%98%AF%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0%EF%BC%9F"><span class="nav-number">1.</span> <span class="nav-text">1：什么是钩子函数？</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2%EF%BC%9AFreeRTOS%E4%B8%AD%E6%8F%90%E4%BE%9B%E7%9A%84%E5%87%A0%E4%B8%AA%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0"><span class="nav-number">2.</span> <span class="nav-text">2：FreeRTOS中提供的几个钩子函数</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#2-1%EF%BC%9AIdle-Hook-Function"><span class="nav-number">2.1.</span> <span class="nav-text">2.1：Idle Hook Function</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#2-2-Tick-Hook-Function"><span class="nav-number">2.2.</span> <span class="nav-text">2.2 Tick Hook Function</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#2-3-Malloc-Failed-Hook-Function"><span class="nav-number">2.3.</span> <span class="nav-text">2.3 Malloc Failed Hook Function</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#2-4-Stack-Overflow-Hook-Function"><span class="nav-number">2.4.</span> <span class="nav-text">2.4 Stack Overflow Hook Function</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">feng xun</p>
  <div class="site-description" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">96</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">16</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="cc-license site-overview-item animated" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" class="cc-opacity" rel="noopener" target="_blank"><img src="https://cdnjs.cloudflare.com/ajax/libs/creativecommons-vocabulary/2020.11.3/assets/license_badges/small/by_nc_sa.svg" alt="Creative Commons"></a>
  </div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="feng xun">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="fx">
      <meta itemprop="description" content="FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="FreeRTOS-内核中的钩子(Hook)函数 | fx">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          FreeRTOS-内核中的钩子(Hook)函数
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-11-14 00:00:00" itemprop="dateCreated datePublished" datetime="2022-11-14T00:00:00+08:00">2022-11-14</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/FreeRTOS/" itemprop="url" rel="index"><span itemprop="name">FreeRTOS</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>6.8k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>8 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <center>
FreeRTOS提供了几个钩子（Hook）函数接口（本质是设置一个回调函数）。如果我们按照钩子函数要求的形式实现它，就可以让内核在特定状态，或内核发生了特定事件时，调用我们实现的钩子函数。
</center>

<span id="more"></span>
<hr>
<h4 id="1：什么是钩子函数？"><a href="#1：什么是钩子函数？" class="headerlink" title="1：什么是钩子函数？"></a>1：什么是钩子函数？</h4><p>钩子函数的本质可以看做是回调函数，例如在Linux上开发时，时常需要处理一些系统信号（signal）,那么就需要调用系统API 注册信号处理函数（回调函数）。如此，当系统发生信号时，就会调用注册的信号处理函数，这种方式是在代码的运行过程中，通过调用 API 动态注册回调函数实现的。</p>
<p>FreeRTOS中，实现钩子函数的方式更简单直接，直接实现定义好的固定函数名字的 Hook 函数即可。如下图所示，实现vApplicationIdleHook 钩子函数就是配置一个宏，然后实现这个函数具体内容：<br><img src="/2022/11/14/FreeRTOS-kernal-hook/hook_register.jpg"></p>
<p>FreeRTOS提供的几个钩子函数是定义好函数名和参数形式的。例如，FreeRTOS中的 idle hook函数，当内核没有其它任务运行时，内核就会运行内核自己创建的 idle 任务，idle任务中会运行 idle hook 函数。当然，运行的前提是，我们要在<code>FreeRTOSConfig.h</code>文件中定义 <strong>configUSE_IDLE_HOOK</strong>，之后再实现函数 <code>void vApplicationIdleHook( void )</code> 的具体内容，函数名和参数形式都是固定的，不能改变。<br><code>这种方式可以看成是静态注册，即在代码的编译阶段，就将对应的 Hook 函数注册好了。</code></p>
<h4 id="2：FreeRTOS中提供的几个钩子函数"><a href="#2：FreeRTOS中提供的几个钩子函数" class="headerlink" title="2：FreeRTOS中提供的几个钩子函数"></a>2：FreeRTOS中提供的几个钩子函数</h4><h5 id="2-1：Idle-Hook-Function"><a href="#2-1：Idle-Hook-Function" class="headerlink" title="2.1：Idle Hook Function"></a>2.1：Idle Hook Function</h5><p>使用 <strong>idle hook</strong> 函数，需要在<code>FreeRTOSConfig.h</code>文件中定义 <strong>configUSE_IDLE_HOOK</strong>，并在工程中实现 <code>void vApplicationIdleHook( void )</code> 函数的具体内容。</p>
<p>FreeRTOS在内核在启动调度程序时，会自动创建一个 <strong>idle 任务</strong>。该任务的优先级默认是0，即最低优先级。当系统中没有其它更高优先级的任务时，FreeRTOS内核就会选择 <strong>idle任务</strong> 运行（如果存在其它优先级也为0 的任务，默认是轮流调度）。idle 任务的大致流程如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">prvIdleTask</span><span class="params">( <span class="type">void</span> *pvParameters )</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">for</span> (; ;) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 释放那些调用 vTaskDelete 删除自己的任务所占用的内核资源</span></span><br><span class="line">        prvCheckTasksWaitingTermination()</span><br><span class="line">        ...</span><br><span class="line">        <span class="meta">#<span class="keyword">if</span> ( configUSE_IDLE_HOOK == 1 )</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">vApplicationIdleHook</span><span class="params">( <span class="type">void</span> )</span>;</span><br><span class="line">                <span class="comment">// 调用 idle hook 函数</span></span><br><span class="line">                vApplicationIdleHook();</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 与低功耗相关的一些处理</span></span><br><span class="line">         <span class="meta">#<span class="keyword">if</span> ( configUSE_TICKLESS_IDLE != 0 )</span></span><br><span class="line">            ....</span><br><span class="line">         <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>idle 任务主要是做三件事：</p>
<ul>
<li><p>调用<code>prvCheckTasksWaitingTermination</code>来释放一些被删除任务的内核资源。FreeRTOS提供的<code>vTaskDelete( TaskHandle_t xTaskToDelete )</code>是可以删除某个任务的，删除任务就会释放相关的内核资源（内核任务控制块，任务栈空间），如果调用 vTaskDelete（<strong>NULL</strong>），就是自己删除自身，一个任务是无法删除自身的，所以FreeRTOS会将那些调用<strong>vTaskDelete(NULL)<strong>删除自己的任务挂到</strong>等待删除任务队列</strong>上。<code>prvCheckTasksWaitingTermination</code>做的事情，就是检查等待删除任务队列上是否有需要删除的任务。更详细的信息可以参考：<a href="https://fengxun2017.github.io/2022/11/01/FreeRTOS-task-status-management/#FreeRTOS%E4%BB%BB%E5%8A%A1%E7%8A%B6%E6%80%81%E5%9C%A8%E5%86%85%E6%A0%B8%E4%B8%8A%E7%9A%84%E5%AE%9E%E9%99%85%E5%AE%9E%E7%8E%B0">FreeRTOS的任务调度和管理</a></p>
<br>
</li>
<li><p>调用 idle hook 函数</p>
<br>  </li>
<li><p>一些低功耗相关的处理</p>
</li>
</ul>
<p>如上所述，我们实现的  <code>void vApplicationIdleHook( void )</code> 函数，就是在 idle 任务中运行的。<br>idle任务是当前没有更高优先级的任务在运行、或处于就绪状态时，内核才会调度 idle 任务。可以认为idle 任务运行时，系统基本处于空闲状态（其高优先级任务应该全部处于阻塞态，或挂起态，否则不会运行最低优先级的任务）。<br>所以，<code>vApplicationIdleHook</code>是放置低功耗相关代码的理想位置。如果，系统对功耗有要求，可以在该函数中关闭相关耗电外设，然后执行 WFI 让CPU进入休眠降低系统功耗，将相关外设的重新打开代码放在WFI 指令之后，使得CPU在被唤醒后可以恢复相关外设功能。<br>（<code>实际上，idle函数中与配置configUSE_TICKLESS_IDLE相关的代码，也做了低功耗的处理，并且考虑的更全面，但并不是所有平台都支持。</code>）</p>
<p>一个需要注意的问题是：<font color="red">绝对不要在vApplicationIdleHook函数中调用会让任务阻塞的FreeRTOS API </font>，例如, 调用vTaskDelay 让任务延迟一会，或者等待诸如消息队列这样的资源，并设置了超时时间（设置了超时时间，当资源不可用时，就会让任务处于阻塞）。<br>这个问题的本质与FreeRTOS内核的实现方式相关。你可以测试一个如下的例子：创建一个优先级为 1 的任务，输出调试信息。实现<code>vApplicationIdleHook</code> 函数，同样输出调试信息，并且也调用了vTaskDelay（这会让调用vApplicationIdleHook函数的 idle任务阻塞）。 代码中的输出函数可以替换成自己的串口输出，或者参考这篇文章使用J-Link RTT：<a href="https://fengxun2017.github.io/2022/10/20/use-jlink-RTT/">使用JLink-RTT输出调试信息</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">test_task</span><span class="params">( <span class="type">void</span> * pvParameters )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">const</span> TickType_t delay = pdMS_TO_TICKS(<span class="number">100</span>); </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">        SEGGER_RTT_printf(<span class="number">0</span>, <span class="string">&quot;in test task\r\n&quot;</span>);</span><br><span class="line">        vTaskDelay( delay );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">vApplicationIdleHook</span><span class="params">( <span class="type">void</span> )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">const</span> TickType_t delay = pdMS_TO_TICKS(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    SEGGER_RTT_printf(<span class="number">0</span>, <span class="string">&quot;in application hook\r\n&quot;</span>);</span><br><span class="line">    vTaskDelay( delay );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    SEGGER_RTT_printf(<span class="number">0</span>, <span class="string">&quot;start main\r\n&quot;</span>);</span><br><span class="line">    xTaskCreate(test_task, <span class="string">&quot;task&quot;</span>, <span class="number">200</span>, <span class="literal">NULL</span>, <span class="number">1</span>, <span class="literal">NULL</span>);      </span><br><span class="line">    vTaskStartScheduler();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span>( ;; );</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>此外，在FreeRTOSConfig.h文件中，定义<strong>configASSERT(X)</strong>, 参数为出现错误的文件和行号，如下所示：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">vAssertCalled</span><span class="params">( <span class="type">const</span> <span class="type">char</span> * pcFile, <span class="type">uint32_t</span> ulLine )</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> configASSERT( x ) <span class="keyword">if</span>( ( x ) == 0 )	vAssertCalled( __FILE__, __LINE__ )</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>vAssertCalled 函数内部实现输出具体信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">vAssertCalled</span><span class="params">( <span class="type">const</span> <span class="type">char</span> * pcFile, <span class="type">uint32_t</span> ulLine )</span>&#123;</span><br><span class="line">    SEGGER_RTT_printf(<span class="number">0</span>, <span class="string">&quot;error at:%s, line:%d\r\n&quot;</span>, pcFile, ulLine);</span><br><span class="line">    <span class="keyword">for</span>(;;)&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>实现上述代码，编译烧录后运行，你可以看到类似下面的调试信息（不同的内核版本，可能报错的行数不一样。）：<br><img src="/2022/11/14/FreeRTOS-kernal-hook/idle-hook-error.png"></p>
<p>根据调试信息，可以定位到系统是在进行任务切换时，选择当前最高优先级的就绪任务时出错了。</p>
<p>具体原因如下：当前系统只有 2 个任务，一个是主动创建的 test_task任务，优先级为 1；一个是内核调度启动时自己创建的 idle 任务，优先级为 0。<br>当内核运行起来后，会先运行优先级为 1 的test_task任务。test_task输出”in test task”，之后由于调用vTaskDelay，进入阻塞态。<br>内核此时进行任务切换，由于只有 idle 任务处于就绪态了，内核选择idle 任务运行。 idle 任务运行后会调用vApplicationIdleHook，输出 “in application hook”，之后同样因为调用vTaskDelay，进入阻塞态。<br>内核此时再次进行任务切换，<strong>但此时没有任务可以运行了！FreeRTOS的内核实现中是不支持这种状态的！</strong><br>所以：<font color="red">绝对不要在vApplicationIdleHook函数中调用会让任务阻塞的FreeRTOS API ！</font></p>
<p>另一个需要注意的点是，<strong>vApplicationIdleHoo 中不要实现无限循环</strong>，因为调用vApplicationIdleHoo 的 idle 任务有清理待删除任务所占内存资源的功能，如果你的系统中有任务会删除自己，并且vApplicationIdleHoo 内部又是无限循环，那么函数就无法退出，idle 任务就不能执行释放待删除任务所占资源的那部分代码。<br><br></p>
<h5 id="2-2-Tick-Hook-Function"><a href="#2-2-Tick-Hook-Function" class="headerlink" title="2.2 Tick Hook Function"></a>2.2 Tick Hook Function</h5><p>使用 Tick HooK 函数需要在<code>FreeRTOSConfig.h</code>文件中定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> configUSE_TICK_HOOK			1</span></span><br></pre></td></tr></table></figure>
<p>并且实现函数 <code>void vApplicationTickHook( void )</code>的具体内容。<br>关于 FreeRTOS的 <strong>tick中断</strong>，可以参考这里：<a href="https://fengxun2017.github.io/2022/11/08/FreeRTOS-%E5%86%85%E6%A0%B8%E5%AF%B9%E6%97%B6%E9%97%B4%E7%9A%84%E6%B5%8B%E9%87%8F/">FreeRTOS-内核对时间的测量</a>。</p>
<p>tick 钩子函数就是在每次 <strong>tick中断</strong> 发生后被调用，cortex-m处理器上使用 systick 定时器来产生 tick中断，在systick定时器的中断处理函数中会调用 <strong>xTaskIncrementTick</strong> ，该函数内部就会调用 tick hook 函数：<font color="red"> vApplicationTickHook </font>。例如，实现一个如下的简单输出调试信息的 tick hook 函数，烧录到开发板上运行后，就可以在不停输出调试信息。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">vApplicationTickHook</span><span class="params">( <span class="type">void</span> )</span></span><br><span class="line">&#123;</span><br><span class="line">    SEGGER_RTT_printf(<span class="number">0</span>, <span class="string">&quot;in tick hook\r\n&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>需要强调的是，<strong>vApplicationTickHook是在中断处理函数中被调用的</strong>。因此，我们实现vApplicationTickHook 的具体内容时，不应该做一些耗时操作，<strong>并且不能调用没有 FromISR 后缀的 FreeRTOS API</strong>（FreeRTOS中很多函数有两个版本，有 FromISR 后缀的才能在中断环境中调用。即使是带是FromISR后缀的API，也只能在硬件中断优先级低于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 的中断中调用，否则可能会破坏在临界区中操作的内核数据结构）。</p>
<p>如果你有一个“简短的” 周期性任务，并且其运行频率基本和内核运行频率一致，那么就可以将其实现在 tick hook 函数中。（需要注意的是，如果设置了configUSE_TICKLESS_IDLE，FreeRTOS会根据系统状态来决定是否要跳过几个 tick中断，保持更长时间的连续休眠，从而节省功耗）<br><br></p>
<h5 id="2-3-Malloc-Failed-Hook-Function"><a href="#2-3-Malloc-Failed-Hook-Function" class="headerlink" title="2.3 Malloc Failed Hook Function"></a>2.3 Malloc Failed Hook Function</h5><p>使用 Malloc Failed Hook 函数需要在<code>FreeRTOSConfig.h</code>文件中定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> configUSE_MALLOC_FAILED_HOOK			1</span></span><br></pre></td></tr></table></figure>
<p>并且实现函数 <code>void vApplicationMallocFailedHook( void )</code>的具体内容。<br>malloc failed hook 函数就是在内存申请失败时，调用我们实现的 <code>vApplicationMallocFailedHook</code> 函数。在FreeRTOS提供的动态内存分配文件 <code>heap_x.c （x=1、2、3、4、5）</code>中，实现了<code>pvPortMalloc</code> API 来申请内存，如果申请失败了，就会调用<code>vApplicationMallocFailedHook</code>。 </p>
<p>该钩子函数用处不大，因为调用<code>pvPortMalloc</code> 的返回值为<code>NULL</code>时，就是告诉我们内存申请失败了。不过，可以在开发阶段使用，直接在vApplicationMallocFailedHook 函数中输出内存不够的调试信息，这样可以避免在每个调用pvPortMalloc的地方去根据返回值，输出调试信息。<br><br></p>
<h5 id="2-4-Stack-Overflow-Hook-Function"><a href="#2-4-Stack-Overflow-Hook-Function" class="headerlink" title="2.4 Stack Overflow Hook Function"></a>2.4 Stack Overflow Hook Function</h5><p>使用 栈溢出检测钩子函数，需要在<code>FreeRTOSConfig.h</code>文件中定义 <strong>configCHECK_FOR_STACK_OVERFLOW（有两种定义的值，代表两种检测模式，后面解释）</strong>，并且实现函数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">vApplicationStackOverflowHook</span><span class="params">( TaskHandle_t xTask, <span class="type">signed</span> <span class="type">char</span> *pcTaskName )</span></span><br></pre></td></tr></table></figure>
<p>其中，<code>xTask</code>为FreeRTOS中的任务句柄（用来识别一个任务），在成功创建一个任务时，内核会返回被创建的这个任务的句柄， <code>pcTaskName</code> 为创建任务时，设置的任务名。任务创建可以参考：<a href="https://fengxun2017.github.io/2022/10/29/FreeRTOS-%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1/">FreeRTOS 创建任务</a>.</p>
<p>栈溢出检测钩子函数，其目的就是为了检测任务栈是否溢出，如果溢出了，就会调用实现的vApplicationStackOverflowHook，并传入任务句柄，和任务名，方便定位是哪个任务发生了栈溢出。</p>
<p>FreeRTOS将任务的栈溢出检测，放在了任务切换中，当目前正在运行的任务被换出时，检测一下它是否有栈溢出。官方给的理由是任务在被换出时，需要在任务栈中保存上下文信息（这样后面再次被调度时才能恢复），这时任务栈可能达到最深。当然，这只是可能。）</p>
<p>FreeRTOS有 2 种栈溢出检测方式：</p>
<ul>
<li><p>当定义 <strong>configCHECK_FOR_STACK_OVERFLOW 为 1</strong> 时，栈检测的方式就是看当前栈指针，有没有超过栈范围。只要栈顶指针所指的地址，没有超过栈空间的边界，那么就算作没溢出。<img src="/2022/11/14/FreeRTOS-kernal-hook/stack-check-1.png"><br>这种检测方式很快，只要比较一下地址就可以了。但这种检测方式是基于 任务被换出时，任务的栈地址达到最深。实际上，任务可能是被换出之前达到最深。例如下面这种例子:</p>
 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">fun_a</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="type">char</span> <span class="built_in">array</span>[<span class="number">1000</span>];</span><br><span class="line">    .....</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">task_a</span><span class="params">( <span class="type">void</span> * pvParameters )</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    fun_a();</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p> 任务 task_a自己没有定义局部的大数组，但是调用了函数 fun_a，函数fun_a 中申请了一个大的临时数组（占用栈空间），那么此时任务调用 fun_a 时会达到任务栈的最深。假设任务 task_a 退出fun_a后（char array[1000]被销毁了，不占空间了），发生了了任务切换，任务task_a 被换出去，那么此时看到的栈顶指针就不是最深时候的了。如果之前发生了栈溢出，此时就检测不到了，那么系统后续运行就可能不正常了（之前的栈溢出可能已经覆盖了某些任务的内核控制块信息，或者是栈信息）。</p>
 <br>
</li>
<li><p>当定义<strong>configCHECK_FOR_STACK_OVERFLOW 为 2</strong> （大于1即可）时，FreeRTOS在创建任务时，会将任务栈空间都设置成一个特殊的初始值(0xa5)。当任务被换出时，内核检查栈的最后16个字节还是不是0xa5，如果不是，说明曾经栈最深时，达到过这里，所以被其它值覆盖了，那么就判定任务栈溢出了。<br> <img src="/2022/11/14/FreeRTOS-kernal-hook/stack-check-2.png"><br> 相比第一种检测方式，只能检测被换出的那个时刻栈有没有溢出。第二种检测方式可以检测到任务在之前的运行过程中，有没有发送过栈溢出。（但这种方式也不是说百分百，如果发生栈溢出时，覆盖了最后16个字节，但覆盖值刚好就是0x5a，不就可以骗过去了，不过基本不用考虑这种特殊的小概率事件）</p>
</li>
</ul>
<p> 需要注意的是：栈溢出的检测最好只在开发阶段进行测试，正式发布的代码最好不要包含该动能，会影响任务切换时的效率（任务切换发生在中断中）。</p>
<br>
ps：需要注意文章代码中的日志输出函数，产品代码中如果需要使用的话，需要考虑线程安全性（多任务安全性），因为中断/任务切换可能发生在另一个任务正在输出日志但还未输出完的时候，这就可能造成日志错乱

<br>
<br>
FreeRTOS交流QQ群-663806972
    </div>

    
    
    

    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>本文作者： </strong>feng xun
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/" title="FreeRTOS-内核中的钩子(Hook)函数">http://fengxun2017.github.io/2022/11/14/FreeRTOS-kernal-hook/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2022/11/08/FreeRTOS-%E5%86%85%E6%A0%B8%E5%AF%B9%E6%97%B6%E9%97%B4%E7%9A%84%E6%B5%8B%E9%87%8F/" rel="prev" title="FreeRTOS-内核对时间的测量">
                  <i class="fa fa-chevron-left"></i> FreeRTOS-内核对时间的测量
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/11/16/FreeRTOS-use-timer/" rel="next" title="FreeRTOS-软件定时器的使用">
                  FreeRTOS-软件定时器的使用 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2025</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">feng xun</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">552k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">11:30</span>
  </span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
